package com.example.testdemo.kinetic.sprite.bullet;

import java.util.ArrayList;
import java.util.List;

import org.andengine.engine.handler.physics.PhysicsHandler;
import org.andengine.entity.sprite.Sprite;
import org.andengine.opengl.texture.region.ITiledTextureRegion;

import com.example.testdemo.TankGameActivity;
import com.example.testdemo.kinetic.interfaces.IOnDestroyTargetListener;
import com.example.testdemo.kinetic.sprite.tank.enemy.EnemyTank1;
import com.example.testdemo.kinetic.sprite.tank.player.PlayerTank1;
import com.example.testdemo.kstatic.sprite.ShieldSprite;
import com.example.testdemo.kstatic.sprite.terrain.WallTerrain;
import com.example.testdemo.pools.BulletPools;
import com.example.testdemo.pools.ShieldPools;

public class Bullet1 extends BaseBullet {

	public static final String BASE_NAME = Bullet1.class.getSimpleName();

	@SuppressWarnings("unused")
	private final String TAG = BASE_NAME;

	public static final float FACE_UP = 0;
	public static final float FACE_DOWN = 180;
	public static final float FACE_RIGHT = 90;
	public static final float FACE_LEFT = 270;

	final float DECTECT_DISTANCE = 64f;

	public Bullet1(BulletPools bulletPools, int whichScene, String owner,
	        String pName, float pX, float pY,
	        ITiledTextureRegion pTiledTextureRegion, TankGameActivity pActivity) {
		super(bulletPools, whichScene, owner, pName, pX, pY,
		        pTiledTextureRegion, pActivity);
		setPhysicsHandler(new PhysicsHandler(this));
	}

	private boolean shouldAddObject(boolean kinetic, Sprite object) {
		final float TOLERANCE = getWidth() > getHeight() ? getWidth() / 2
		        : getHeight() / 2;
		final float COLLIDE_DISTANCE;
		if (kinetic) {
			COLLIDE_DISTANCE = 200f;
		} else {
			COLLIDE_DISTANCE = 64;
		}

		final float bCenterX = getX() + getWidth() / 2;
		final float bCenterY = getY() + getHeight() / 2;
		final float tCenterX = object.getX() + object.getWidth() / 2;
		final float tCenterY = object.getY() + object.getHeight() / 2;

		final float dir = getRotation();
		if (dir == FACE_DOWN) {
			if ((object.getY() + TOLERANCE) - getY() >= 0) {
				if (Math.abs(tCenterX - bCenterX) <= COLLIDE_DISTANCE) {
					return true;
				}
			}
		} else if (dir == FACE_UP) {
			if ((object.getY() - TOLERANCE) - getY() <= 0) {
				if (Math.abs(tCenterX - bCenterX) <= COLLIDE_DISTANCE) {
					return true;
				}
			}
		} else if (dir == FACE_LEFT) {
			if ((object.getX() - TOLERANCE) - getX() <= 0) {
				if (Math.abs(tCenterY - bCenterY) <= COLLIDE_DISTANCE) {
					return true;
				}
			}
		} else if (dir == FACE_RIGHT) {
			if ((object.getX() + TOLERANCE) - getX() >= 0) {
				if (Math.abs(tCenterY - bCenterY) <= COLLIDE_DISTANCE) {
					return true;
				}
			}
		}

		return false;
	}

	// call by super.fire()
	@Override
	protected void generatePredictCollisionList() {
		if (mPredictCollisionList.size() > 0) {
			mPredictCollisionList.clear();
		}

		final boolean isPlayerBullet = mOwner.startsWith(PlayerTank1.BASE_NAME);

		// Add enemy & enemy bullet
		if (isPlayerBullet) {
			List<Sprite> enemyList = new ArrayList<Sprite>();
			for (int i = mOnScene.getSpriteTag().ENEMY_TANK_START; i <= mOnScene
			        .getSpriteTag().ENEMY_TANK_END; i++) {
				final EnemyTank1 tank = (EnemyTank1) mOnScene.getChildByTag(i);
				if (tank == null) {
					continue;
				} else if (!tank.isVisible()) {
					enemyList.add(tank);
				} else {
					if (shouldAddObject(true, tank)) {
						enemyList.add(tank);
					}
				}
				enemyList.add(tank);
			}
			mPredictCollisionList.put(ObjectType.ENEMY, enemyList);
			
			List<Sprite> enemyBullets = BulletPools.getInstance(mWhichScene).getQueue(BulletPools.ENEMY_BULLET_1);
			mPredictCollisionList.put(ObjectType.ENEMY_BULLET, enemyBullets);

			// Add Players
		} else {
			List<Sprite> playerList = new ArrayList<Sprite>();
			for (int i = mOnScene.getSpriteTag().PLAYER_TANK_START; i <= mOnScene
			        .getSpriteTag().PLAYER_TANK_END; i++) {

				final PlayerTank1 player = (PlayerTank1) mOnScene
				        .getChildByTag(i);
				if (player == null) {
					continue;
				} else if (!player.isVisible()) {
					playerList.add(player);
				} else {
					if (shouldAddObject(true, player)) {
						playerList.add(player);
					}
				}
			}
			mPredictCollisionList.put(ObjectType.PLAYER, playerList);
		}

		// add walls
		List<Sprite> wallList = new ArrayList<Sprite>();
		for (int i = mOnScene.getSpriteTag().WALL_START; i <= mOnScene
		        .getSpriteTag().WALL_END; i++) {
			Sprite wall = (Sprite) mOnScene.getChildByTag(i);
			if (wall == null || !wall.isVisible()) {
				continue;
			}

			if (shouldAddObject(false, wall)) {
				wallList.add(wall);
			}
		}
		mPredictCollisionList.put(ObjectType.WALL, wallList);

		// add obstacles
		List<Sprite> obstacleList = new ArrayList<Sprite>();
		for (int i = mOnScene.getSpriteTag().OBSTACLE_START; i <= mOnScene
		        .getSpriteTag().OBSTACLE_END; i++) {
			Sprite obstacle = (Sprite) mOnScene.getChildByTag(i);
			if (obstacle == null || !obstacle.isVisible()) {
				continue;
			}

			if (shouldAddObject(false, obstacle)) {
				obstacleList.add(obstacle);
			}
		}
		mPredictCollisionList.put(ObjectType.OBSTACLE, obstacleList);

		// add heart
		List<Sprite> heartList = new ArrayList<Sprite>();
		Sprite heart = (Sprite) mOnScene
		        .getChildByTag(mOnScene.getSpriteTag().HEART);
		if (heart != null) {
			if (shouldAddObject(false, heart)) {
				heartList.add(heart);
			}
		}
		mPredictCollisionList.put(ObjectType.HEART, heartList);
	}

	@Override
	protected boolean resolvedWallCollision(List<Sprite> wallList) {
		for (Sprite wall : wallList) {
			if (!wall.isVisible()) {
				continue;
			}

			if (Math.abs(wall.getY() - getY()) > DECTECT_DISTANCE) {
				continue;
			} else {
				if (Math.abs(wall.getX() - getX()) > DECTECT_DISTANCE) {
					continue;
				}
			}

			boolean collide = false;
			if (getScaleX() == 1f) {
				collide = fastCollidesWith(wall);
			} else {
				collide = collidesWith(wall);
			}

			if (collide) {
				final WallTerrain wallTerrain = (WallTerrain) wall;

				int diff = mData.getStrength() - wallTerrain.mData.getDefence();
				if (diff >= 0) {
					mData.setStrength(diff);
					wallTerrain.mData.setDefence(0);
				} else {
					mData.setStrength(0);
					wallTerrain.mData.setDefence(Math.abs(diff));
				}

				if (wallTerrain.mData.getDefence() <= 0) {
					mOnScene.recycleTMXTileSprite(wall);
					mOnScene.transformToGroundTile(wall);
					for (IOnDestroyTargetListener listener : mListenerMap
					        .values()) {
						listener.onTargetDestroyed(ObjectType.WALL, wall);
					}
				} else {
					ShieldSprite shield = ShieldPools.getInstance(mActivity,
					        mOnScene).pollOneSheild();
					shield.soud();
					shield.animate(wall, 50);
				}

				if (mData.getStrength() <= 0) {
					mBulletPools.recycle(this);
					return true; // bullet is destroyed, we should return in
					             // onManagedUpdate
				}
			}
		}

		return false;
	}

	@Override
	protected boolean resolvedObstacleCollision(List<Sprite> obstacleList) {
		for (Sprite obstacle : obstacleList) {
			if (!obstacle.isVisible()) {
				continue;
			}

			if (Math.abs(obstacle.getY() - getY()) > DECTECT_DISTANCE) {
				continue;
			} else {
				if (Math.abs(obstacle.getX() - getX()) > DECTECT_DISTANCE) {
					continue;
				}
			}

			boolean collide = false;
			if (getScaleX() == 1f) {
				collide = fastCollidesWith(obstacle);
			} else {
				collide = collidesWith(obstacle);
			}

			if (collide) {
				mBulletPools.recycle(this);
				return true;
			}
		}

		return false;
	}
	
	@Override
	protected boolean resolvedEnemyBulletCollision(List<Sprite> enemyBullets) {
		for (Sprite bullet : enemyBullets) {
			if (bullet != null && bullet.isVisible()) {
				if (Math.abs(bullet.getY() - getY()) > DECTECT_DISTANCE) {
					continue;
				} else {
					if (Math.abs(bullet.getX() - getX()) > DECTECT_DISTANCE) {
						continue;
					}
				}
				
				boolean collide = false;
				if (getScaleX() == 1f && bullet.getScaleX() == 1f) {
					collide = fastCollidesWith(bullet);
				} else {
					collide = collidesWith(bullet);
				}

				if (collide) {
					final Bullet1 enemyBullet = (Bullet1) bullet;
					
					int diff = mData.getStrength() - enemyBullet.mData.getStrength();
					if (diff >= 0) {
						mData.setStrength(diff);
						enemyBullet.mData.setStrength(0);
					} else {
						mData.setStrength(0);
						enemyBullet.mData.setStrength(Math.abs(diff));
					}

					if (enemyBullet.mData.getStrength() <= 0) {
						mBulletPools.recycle(this);
					}

					if (mData.getStrength() <= 0) {
						mBulletPools.recycle(this);
						return true;
					}
				}
			}
		}
		
		return false;
	}

	@Override
	protected boolean resolvedEnemyCollision(List<Sprite> enemyList) {
		for (Sprite sprite : enemyList) {

			final EnemyTank1 tank = (EnemyTank1) sprite;
			if (tank == null || tank.isDestroyed() || !tank.isVisible()) {
				continue;

			}

			if (Math.abs(tank.getY() - getY()) > DECTECT_DISTANCE) {
				continue;
			} else {
				if (Math.abs(tank.getX() - getX()) > DECTECT_DISTANCE) {
					continue;
				}
			}

			boolean collide = false;
			if (getScaleX() == 1f && tank.getScaleX() == 1f) {
				collide = fastCollidesWith(tank);
			} else {
				collide = collidesWith(tank);
			}

			if (collide) {
				int diff = mData.getStrength() - tank.mData.getDefence();
				if (diff >= 0) {
					mData.setStrength(diff);
					tank.mData.setDefence(0);
				} else {
					mData.setStrength(0);
					tank.mData.setDefence(Math.abs(diff));
				}

				if (tank.mData.getDefence() <= 0) {
					destroyEnemy(tank);
				} else {
					ShieldSprite shield = ShieldPools.getInstance(mActivity,
					        mOnScene).pollOneSheild();
					shield.soud();
					shield.animate(tank, 50);
				}

				if (mData.getStrength() <= 0) {
					mBulletPools.recycle(this);
					return true;
				}
			}
		}

		return false;
	}

	@Override
	protected boolean resolvedPlayerCollision(List<Sprite> playerList) {
		for (Sprite sprite : playerList) {

			final PlayerTank1 player = (PlayerTank1) sprite;
			if (player == null || player.isDestroyed() || !player.isVisible()
			        || player.isInvincible()) {
				continue;

			}

			if (Math.abs(player.getY() - getY()) > DECTECT_DISTANCE) {
				continue;
			} else {
				if (Math.abs(player.getX() - getX()) > DECTECT_DISTANCE) {
					continue;
				}
			}

			boolean collide = false;
			if (getScaleX() == 1f && player.getScaleX() == 1f) {
				collide = fastCollidesWith(player);
			} else {
				collide = collidesWith(player);
			}

			if (collide) {
				int diff = mData.getStrength() - player.mData.getDefence();
				if (diff >= 0) {
					mData.setStrength(diff);
					player.mData.setDefence(0);
				} else {
					mData.setStrength(0);
					player.mData.setDefence(Math.abs(diff));
				}

				if (player.mData.getDefence() <= 0) {
					destroyPlayer(player);
				} else {
					ShieldSprite shield = ShieldPools.getInstance(mActivity,
					        mOnScene).pollOneSheild();
					shield.soud();
					shield.animate(player, 50);
				}

				if (mData.getStrength() <= 0) {
					mBulletPools.recycle(this);
					return true;
				}
			}
		}

		return false;
	}

	@Override
	protected boolean resolvedHeartCollision(List<Sprite> heartList) {
		for (Sprite heart : heartList) {
			if (Math.abs(heart.getY() - getY()) < DECTECT_DISTANCE) {
				if (Math.abs(heart.getX() - getX()) < DECTECT_DISTANCE) {

					boolean collide = false;
					if (getScaleX() == 1f) {
						collide = fastCollidesWith(heart);
					} else {
						collide = collidesWith(heart);
					}

					if (collide) {
						mOnScene.recycleTMXTileSprite(heart);
						mOnScene.transformToGroundTile(heart);

						for (IOnDestroyTargetListener listener : mListenerMap
						        .values()) {
							listener.onTargetDestroyed(ObjectType.HEART, heart);
						}

						final int bStrength = mData.getStrength() - 1;
						if (bStrength <= 0) {
							mBulletPools.recycle(this);
							return true;
						}
					}
				}
			}
		}
		return false;
	}

	@Override
	protected boolean fastCollidesWith(Sprite target) {

		boolean tlIn = isPointInRectancle(getX(), getY(), target);
		boolean trIn = isPointInRectancle(getX() + getWidth(), getY(), target);
		boolean blIn = isPointInRectancle(getX(), getY() + getHeight(), target);
		boolean brIn = isPointInRectancle(getX() + getWidth(), getY()
		        + getHeight(), target);

		return tlIn || trIn || blIn || brIn;
	}

	private boolean isPointInRectancle(float x, float y, Sprite target) {
		if (x >= target.getX() && x <= target.getX() + target.getWidth()) {
			if (y >= target.getY() && y <= target.getY() + target.getHeight()) {
				return true;
			}
		}
		return false;
	}

	private void destroyPlayer(PlayerTank1 player) {
		// Log.d(TAG, "destroyPlayer");
		player.destroyed();
		for (IOnDestroyTargetListener listener : mListenerMap.values()) {
			listener.onTargetDestroyed(ObjectType.PLAYER, player);
		}
	}

	private void destroyEnemy(EnemyTank1 enemy) {
		enemy.destroyed();
		for (IOnDestroyTargetListener listener : mListenerMap.values()) {
			listener.onTargetDestroyed(ObjectType.ENEMY, enemy);
		}
	}

	public void free() {
		super.free();
		mActivity.runOnUiThread(new Runnable() {
			@Override
			public void run() {
				// destroy sprite
				mOnScene.detachChild(Bullet1.this);
				dispose();
			}
		});
	}

}
